import uuid
import datetime
from typing import List, Optional, Dict, Any
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from pydantic import BaseModel

# 定义常量
SECRET_KEY = "your-secret-key"  # 实际应用中应使用环境变量或配置文件
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# 初始化密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# 模拟数据库 - 在实际应用中应替换为真实数据库
class FakeDB:
    def __init__(self):
        self.users = {}
        self.roles = {}
        self.permissions = {}
        self.user_roles = {}
        self.role_permissions = {}
        
        # 初始化默认角色和权限
        self.init_default_roles_and_permissions()
        
    def init_default_roles_and_permissions(self):
        # 创建默认权限
        admin_perm = self.create_permission("admin:all", "所有权限")
        user_perm = self.create_permission("user:read", "读取权限")
        write_perm = self.create_permission("user:write", "写入权限")
        admin_manage_perm = self.create_permission("admin:manage", "管理员后台管理权限")
        
        # 创建默认角色
        admin_role = self.create_role("admin", "管理员")
        user_role = self.create_role("user", "普通用户")
        
        # 分配权限给角色
        self.add_permission_to_role(admin_role['id'], admin_perm['id'])
        self.add_permission_to_role(admin_role['id'], admin_manage_perm['id'])
        self.add_permission_to_role(user_role['id'], user_perm['id'])
        
        # 创建默认管理员用户
        self.create_user(
            username="admin",
            email="admin@example.com",
            password="admin123",
            role_ids=[admin_role['id']]
        )

    def create_user(self, username: str, email: str, password: str, role_ids: List[str] = None) -> Dict[str, Any]:
        user_id = str(uuid.uuid4())
        hashed_password = pwd_context.hash(password)
        
        user = {
            "id": user_id,
            "username": username,
            "email": email,
            "hashed_password": hashed_password,
            "is_active": True,
            "created_at": datetime.datetime.now()
        }
        
        self.users[user_id] = user
        
        # 分配角色
        if role_ids:
            self.user_roles[user_id] = role_ids
        
        return user

    def get_user_by_username(self, username: str) -> Optional[Dict[str, Any]]:
        for user in self.users.values():
            if user["username"] == username:
                return user
        return None

    def get_user_by_id(self, user_id: str) -> Optional[Dict[str, Any]]:
        return self.users.get(user_id)

    def create_role(self, name: str, description: str) -> Dict[str, Any]:
        role_id = str(uuid.uuid4())
        
        role = {
            "id": role_id,
            "name": name,
            "description": description,
            "created_at": datetime.datetime.now()
        }
        
        self.roles[role_id] = role
        return role

    def get_role_by_name(self, name: str) -> Optional[Dict[str, Any]]:
        for role in self.roles.values():
            if role["name"] == name:
                return role
        return None

    def create_permission(self, name: str, description: str) -> Dict[str, Any]:
        permission_id = str(uuid.uuid4())
        
        permission = {
            "id": permission_id,
            "name": name,
            "description": description,
            "created_at": datetime.datetime.now()
        }
        
        self.permissions[permission_id] = permission
        return permission

    def add_permission_to_role(self, role_id: str, permission_id: str):
        if role_id not in self.role_permissions:
            self.role_permissions[role_id] = []
        
        if permission_id not in self.role_permissions[role_id]:
            self.role_permissions[role_id].append(permission_id)

    def add_role_to_user(self, user_id: str, role_id: str):
        if user_id not in self.user_roles:
            self.user_roles[user_id] = []
        
        if role_id not in self.user_roles[user_id]:
            self.user_roles[user_id].append(role_id)

    def get_user_permissions(self, user_id: str) -> List[Dict[str, Any]]:
        permissions = []
        
        # 获取用户角色
        role_ids = self.user_roles.get(user_id, [])
        
        # 获取角色权限
        for role_id in role_ids:
            permission_ids = self.role_permissions.get(role_id, [])
            for permission_id in permission_ids:
                permission = self.permissions.get(permission_id)
                if permission:
                    permissions.append(permission)
        
        return permissions

    def user_has_permission(self, user_id: str, permission_name: str) -> bool:
        permissions = self.get_user_permissions(user_id)
        return any(perm["name"] == permission_name or perm["name"] == "admin:all" for perm in permissions)

# 初始化模拟数据库
fake_db = FakeDB()

# 定义Pydantic模型
class UserBase(BaseModel):
    username: str
    email: str
    
class UserCreate(UserBase):
    password: str

class User(UserBase):
    id: str
    is_active: bool
    
    class Config:
        orm_mode = True

class Token(BaseModel):
    access_token: str
    token_type: str

class TokenData(BaseModel):
    username: Optional[str] = None

# 认证工具函数
def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
    return pwd_context.hash(password)

def authenticate_user(fake_db, username: str, password: str):
    user = fake_db.get_user_by_username(username)
    if not user:
        return False
    if not verify_password(password, user["hashed_password"]):
        return False
    return user

def create_access_token(data: dict, expires_delta: Optional[datetime.timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.datetime.utcnow() + expires_delta
    else:
        expire = datetime.datetime.utcnow() + datetime.timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

# 依赖项
async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except JWTError:
        raise credentials_exception
    user = fake_db.get_user_by_username(username=token_data.username)
    if user is None:
        raise credentials_exception
    return user

def get_current_active_user(current_user: Dict[str, Any] = Depends(get_current_user)):
    if not current_user["is_active"]:
        raise HTTPException(status_code=400, detail="Inactive user")
    return current_user

def check_permission(permission_name: str):
    def permission_dependency(current_user: Dict[str, Any] = Depends(get_current_active_user)):
        if not fake_db.user_has_permission(current_user["id"], permission_name):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Not enough permissions"
            )
        return current_user
    return permission_dependency

# 认证路由函数
def get_auth_routes(app):
    @app.post("/token", response_model=Token)
    async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
        user = authenticate_user(fake_db, form_data.username, form_data.password)
        if not user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Incorrect username or password",
                headers={"WWW-Authenticate": "Bearer"},
            )
        access_token_expires = datetime.timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        access_token = create_access_token(
            data={"sub": user["username"]},
            expires_delta=access_token_expires
        )
        return {"access_token": access_token, "token_type": "bearer"}

    @app.post("/users", response_model=User)
    async def create_user(user: UserCreate):
        # 检查用户名是否已存在
        existing_user = fake_db.get_user_by_username(user.username)
        if existing_user:
            raise HTTPException(status_code=400, detail="Username already registered")
        
        # 创建新用户
        db_user = fake_db.create_user(
            username=user.username,
            email=user.email,
            password=user.password
        )
        
        return {
            "id": db_user["id"],
            "username": db_user["username"],
            "email": db_user["email"],
            "is_active": db_user["is_active"]
        }

    @app.get("/users/me", response_model=User)
    async def read_users_me(current_user: Dict[str, Any] = Depends(get_current_active_user)):
        return {
            "id": current_user["id"],
            "username": current_user["username"],
            "email": current_user["email"],
            "is_active": current_user["is_active"]
        }

    return app